/*
 * aprioriall.cpp
 *
 *  Created on: Mar 24, 2011
 *      Author: cookiemouse
 */

#include<iostream>
#include<ostream>
#include<vector>
#include<set>
#include<sstream>
#include<math.h>
#include<map>
#include "sequence.h"

using namespace std;

class aprioriAll{
     int currentLayer;
     vector<Pattern*> currentPatterns;
     vector<Pattern*> nextLayerPatternCandidates;
     vector<Pattern*> nextLayerPatterns;
     vector<sequence> db;
     map<int, int> currentHash;
     map<int, int> nextHash;
     map<int,int>::iterator it;
     int timegap;
     int support;
     int grid ;
     int missedByHash;
     int hashBucket;

public:

     double tuplesScanned;
     double candidates;

     void init(vector<sequence> seqs, int timegap, int support, int cgrid, int bucket){
          this->timegap = timegap;
          this->support = support;
          this->db = seqs;
          this->currentLayer =1;
          this->grid= cgrid;
          this->missedByHash = 0;
          this->tuplesScanned = 0;

          this->candidates = 0;
          this->hashBucket = bucket;
          this->generatingFrequentOne();
          processHashToNextLayer();
     }


     void filterCandidates(){
    	 int localTuplesScanned = 0;
         vector<int> supports;
         supports.resize(nextLayerPatternCandidates.size());
         for (int i=0; i<db.size(); i++){
             sequence s = db.at(i);
             for (int j=0; j<nextLayerPatternCandidates.size(); j++){
            	 localTuplesScanned = localTuplesScanned + s.length();
               if (s.complyWith(*nextLayerPatternCandidates[j])){
                  int s =  supports[j];
                  supports[j] = ++s;
               }
             }
         }
         tuplesScanned += localTuplesScanned/(double)1000;


         for (int i=0; i<nextLayerPatternCandidates.size(); i++){
             if (supports[i]>=support){
                  //cout<< nextLayerPatternCandidates[i]<<endl;
                  nextLayerPatterns.push_back(nextLayerPatternCandidates[i]);
             }else {
                   this->missedByHash++;
                   Pattern* p = nextLayerPatternCandidates[i];
//                   if (p->length()==2){
//                     for (int j=0; j<p->length(); j++){
//                       PatternNode* n = p->nodes[j];
//                       delete n;
//                     }
//                   }
                   delete p;
             }
         }
         cout<< "missedByHash!"<<missedByHash<<endl;
         missedByHash = 0;
         nextLayerPatternCandidates.clear();

     }


     void generatingFrequentOne(){
          set<string> freq1p;
          vector<int> count;
          vector<PatternNode> pns;
          for (int i=0; i<db.size();i++){
              sequence s = db.at(i);
              set<string> check;
              processSequenceToHash(s);
              for (int j=0; j<s.length(); j++){
                  tuplesScanned ++;
            	  //cout<< j<< endl;
                  PatternNode pn = s.at(j).toPatternNode();
                  string pnstr = pn.tostring();
                  if (freq1p.find(pnstr)!=freq1p.end() && check.find(pnstr)==check.end()){
                       for (int k=0; k<pns.size(); k++){
                           if ((pns.at(k))== pn ){
                               int sup = count.at(k);
                               count[k] = sup+1;
                           }
                       }
                  }else {
                       if (check.find(pnstr)==check.end()) {
                         freq1p.insert(pnstr);
                         check.insert(pnstr);
                         pns.push_back(pn);
                         count.resize(pns.size());
                         count[pns.size()-1] = 1;
                       }
                  }
              }
          }
          tuplesScanned = tuplesScanned/ (double)1000;
          for (int i=0; i<pns.size(); i++){
              if (count[i]>=support){
                  Pattern* p = new Pattern();
                  p->addNode(pns[i]);
                  this->currentPatterns.push_back(p);
                  cout<<*p<<endl;
              }
          }
     }

     void level2Mining(){
           this->currentLayer =2;
          for (int i=0; i<currentPatterns.size(); i++ ){
              for (int j=0; j<currentPatterns.size(); j++ ){
                  if (i!=j){
                    Pattern* p1 = currentPatterns.at(i);
                    Pattern* p2 = currentPatterns.at(j);
                    for (int t=0; t<timegap; t++){
                        Pattern* p = new Pattern();
                        PatternNode node1 = PatternNode(p1->nodes.at(0));
                        PatternNode node2 = PatternNode(p2->nodes.at(0));
                        bool qualified = false;
                        if (node1.isNeighbor(node2)){
                          node1.gap = t;
                          node2.gap = -1;
                          p->addNode(node1);
                          p->addNode(node2);

                          int ph = this->hashpattern(*p);

                         cout<< *p<<","<<ph <<","<<currentHash[ph]<<endl;
                          if (currentHash[ph]>=support){
                           // cout<<*p<<endl;
                              nextLayerPatternCandidates.push_back(p);
                              qualified = true;

                          }else {
                               //cout<<"hash hit"<<endl;
                          }
                        }
                        if (!qualified){
                           //clear the memory used
                          delete p;
                        }
                    }
                  }
              }
          }

     }

     bool toNextLayer(){
          if (this->nextLayerPatternCandidates.size()>1){
                return true;
          }else {
        	  return false;
          }
     }
     /** not so good implementation*/
     void finishThisLayer(){
          cout<< "layer:"<<this->currentLayer<<",candidates:"<<this->nextLayerPatternCandidates.size()<<endl;
          int curSize = this->currentPatterns.size();
          this->candidates += nextLayerPatternCandidates.size()/(double)1000;
          for (int i=0; i<curSize; i++){
              Pattern* p = currentPatterns.at(currentPatterns.size()-1);
              currentPatterns.pop_back();
              delete p;
          }
          filterCandidates();
          int nextLayerSize = nextLayerPatterns.size();
          for (int i=0; i<nextLayerSize; i++){
              Pattern* p = nextLayerPatterns.at(nextLayerPatterns.size()-1);
              nextLayerPatterns.pop_back();
              cout<< *p << endl;
              currentPatterns.push_back(p);
          }
          processHashToNextLayer();
     }

     void processHashToNextLayer(){
           this->currentHash.clear();
           for ( it=nextHash.begin() ; it != nextHash.end(); it++ ){
              currentHash[(*it).first] = (*it).second ;
           }
           nextHash.clear();
     }

     /** to do: clear next Level and add them to currentLevel**/
     void levelKMining(){
          this->currentLayer++;
           for (int i=0; i<currentPatterns.size(); i++ ){
               //level k merge only need lower half matrix
              for (int j=i+1; j<currentPatterns.size(); j++ ){
                  if (i!=j){
                       Pattern* p1 = currentPatterns.at(i);
                       Pattern* p2 = currentPatterns.at(j);
                       Pattern* p = p1->patternGrow(*p2);
                       if (p->length()>0){
                         nextLayerPatternCandidates.push_back(p);
                       }else {
                    	   delete p;
                       }
                  }
              }
           }
           if (toNextLayer()){
                 finishThisLayer();
                 levelKMining();
           }else {
                 finishThisLayer();
                 return;
           }
     }
     void mining(){
          level2Mining();
          if (toNextLayer()){
             finishThisLayer();
             levelKMining();
          }else {
             finishThisLayer();
          }

     }

     int hashpattern(Pattern &pattern){
         int r = 0;
        for (int i=0; i<pattern.length();i++){
            int p =  pow(10, (int)(pattern.length()-i-1));
            int sr = 0;
            sr+= pattern.nodes.at(i).x*grid;
            sr+= pattern.nodes.at(i).y;
            sr+=  pattern.nodes.at(i).gap*13;
            r += sr*p;
        }
        return r % this->hashBucket;
    }

     void processSequenceToHash(sequence &s){
         //gen next layer hash
         //cout<<"s:" <<s.length()<< endl;
         for (int i=0; i<s.length()-currentLayer-1; i++){
             Pattern pattern;
             for (int j=i; j<i+currentLayer+1; j++){
                 PatternNode node;
                 if (j<i+currentLayer){
                   node= s.at(j).toPatternNode(true);
                 }else {
                   node= s.at(j).toPatternNode(false);
                 }
                 pattern.addNode(node);
             }

             int phash = hashpattern(pattern);
             //cout<<pattern <<":"<<phash << endl;
             int cValue = this->nextHash[phash];
             this->nextHash[phash] = ++cValue;
             //cout<<phash<<","<<cValue<<endl;
//             for (int j=0; j<currentLayer+1; j++){
//                delete pattern.nodes.at(j);
//             }
         }
    }

};







